#pragma once
#include "posix.h"
#include "dbformat.h"

// DB contents are stored in a set of blocks, each of which holds a
// sequence of key,value pairs.  Each block may be compressed before
// being stored in a file.  The following enum describes which
// compression method (if any) is used to compress a block.
enum CompressionType
{
	// NOTE: do not change the values of existing entries, as these are
	// part of the persistent format on disk.
	kNoCompression = 0x0,
	kSnappyCompression = 0x1
};

// Options to control the behavior of a database (passed to DB::Open)
struct Options
{
	// -------------------
	// Parameters that affect behavior

	// Comparator used to define the order of keys in the table.
	// Default: a comparator that uses lexicographic byte-wise ordering
	//
	// REQUIRES: The client must ensure that the comparator supplied
	// here has the same name and orders keys *exactly* the same as the
	// comparator provided to previous open calls on the same DB.
	std::shared_ptr<Comparator> comparator;

	// If true, the database will be created if it is missing.
	// Default: false
	bool createIfMissing;
	// If true, an error is raised if the database already exists.
	// Default: false
	bool errorIfExists;
	// If true, the implementation will do aggressive checking of the
	// data it is processing and will stop early if it detects any
	// errors.  This may have unforeseen ramifications: for example, a
	// corruption of one DB entry may cause a large number of entries to
	// become unreadable or for the entire DB to become unopenable.
	// Default: false
	bool paranoidChecks;

	// Use the specified object to interact with the environment,
	// e.g. to read/write files, schedule background work, etc.
	// Default: Env::Default()
	std::shared_ptr<PosixEnv> env;

	// -------------------
	// Parameters that affect performance

	// Amount of data to build up in memory (backed by an unsorted log
	// on disk) before converting to a sorted on-disk file.
	//
	// Larger values increase performance, especially during bulk loads.
	// Up to two write buffers may be held in memory at the same time,
	// so you may wish to adjust this parameter to control memory usage.
	// Also, a larger write buffer will result in a longer recovery time
	// the next time the database is opened.
	//
	// Default: 4MB
	size_t writeBufferSize;

	// Number of open files that can be used by the DB.  You may need to
	// increase this if your database has a large working set (budget
	// one open file per 2MB of working set).
	//
	// Default: 1000
	int maxOpenFiles;
	// Approximate size of user data packed per block.  Note that the
	// block size specified here corresponds to uncompressed data.  The
	// actual size of the unit read from disk may be smaller if
	// compression is enabled.  This parameter can be changed dynamically.
	//
	// Default: 4K
	size_t blockSize;

	// Number of keys between restart points for delta encoding of keys.
	// This parameter can be changed dynamically.  Most clients should
	// leave this parameter alone.
	//
	// Default: 16
	int blockRestartInterval;

	// Leveldb will write up to this amount of bytes to a file before
	// switching to a new one.
	// Most clients should leave this parameter alone.  However if your
	// filesystem is more efficient with larger files, you could
	// consider increasing the value.  The downside will be longer
	// compactions and hence longer latency/performance hiccups.
	// Another reason to increase this parameter might be when you are
	// initially populating a large database.
	//
	// Default: 2MB
	size_t maxFileSize;

	// Compress blocks using the specified compression algorithm.  This
	// parameter can be changed dynamically.
	//
	// Default: kSnappyCompression, which gives lightweight but fast
	// compression.
	//
	// Typical speeds of kSnappyCompression on an Intel(R) Core(TM)2 2.4GHz:
	//    ~200-500MB/s compression
	//    ~400-800MB/s decompression
	// Note that these speeds are significantly faster than most
	// persistent storage speeds, and therefore it is typically never
	// worth switching to kNoCompression.  Even if the input data is
	// incompressible, the kSnappyCompression implementation will
	// efficiently detect that and will switch to uncompressed mode.
	CompressionType compression;

	// EXPERIMENTAL: If true, append to existing MANIFEST and log files
	// when a database is opened.  This can significantly speed up open.
	//
	// Default: currently false, but may become true later.
	bool reuseLogs;

	// Create an Options object with default values for all fields.
	Options()
		:comparator(new BytewiseComparatorImpl()),
		createIfMissing(false),
		errorIfExists(false),
		paranoidChecks(false),
		env(new PosixEnv()),
		writeBufferSize(4 << 20),
		maxOpenFiles(1000),
		blockSize(4096),
		blockRestartInterval(16),
		maxFileSize(2 << 20),
		compression(kNoCompression),
		reuseLogs(false)
	{

	}

	~Options()
	{

	}
};


// Options that control read operations
struct ReadOptions
{
	// If true, all data read from underlying storage will be
	// verified against corresponding checksums.
	// Default: false
	bool verifyChecksums;

	// Should the data read for this iteration be cached in memory?
	// Callers may wish to set this field to false for bulk scans.
	// Default: true
	bool fillCache;

	ReadOptions()
		: verifyChecksums(true),
		fillCache(true)
	{

	}
};

// Options that control write operations
struct WriteOptions
{
	// If true, the write will be flushed from the operating system
	// buffer cache (by calling WritableFile::Sync()) before the write
	// is considered complete.  If this flag is true, writes will be
	// slower.
	//
	// If this flag is false, and the machine crashes, some recent
	// writes may be lost.  Note that if it is just the process that
	// crashes (i.e., the machine does not reboot), no writes will be
	// lost even if sync==false.
	//
	// In other words, a DB write with sync==false has similar
	// crash semantics as the "write()" system call.  A DB write
	// with sync==true has similar crash semantics to a "write()"
	// system call followed by "fsync()".
	//
	// Default: false
	bool sync;

	WriteOptions()
		:sync(false)
	{

	}
};

enum RecordType
{
	// Zero is reserved for preallocated files
	kZeroType = 0,

	kFullType = 1,

	// For fragments
	kFirstType = 2,
	kMiddleType = 3,
	kLastType = 4
};

static const int kMaxRecordType = kLastType;

static const int kBlockSize = 32768;

// Header is checksum (4 bytes), length (2 bytes), type (1 byte).
static const int kHeaderSize = 4 + 2 + 1;




